package com.pb.wkflow.core.service.impl;

import com.pb.wkflow.api.service.WfSysProcInfoService;
import com.pb.wkflow.core.service.WorkflowMultiService;
import com.pb.wkflow.core.utils.JuelUtils;
import org.camunda.bpm.engine.HistoryService;
import org.camunda.bpm.engine.RuntimeService;
import org.camunda.bpm.engine.TaskService;
import org.camunda.bpm.engine.delegate.DelegateTask;
import org.camunda.bpm.engine.history.HistoricTaskInstance;
import org.camunda.bpm.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.camunda.bpm.engine.impl.interceptor.Command;
import org.camunda.bpm.engine.impl.interceptor.CommandContext;
import org.camunda.bpm.engine.impl.interceptor.CommandExecutor;
import org.camunda.bpm.engine.impl.persistence.entity.TaskEntity;
import org.camunda.bpm.engine.impl.persistence.entity.VariableInstanceEntity;
import org.camunda.bpm.engine.task.Task;
import org.camunda.bpm.engine.variable.impl.value.PrimitiveTypeValueImpl;
import org.camunda.bpm.model.bpmn.BpmnModelInstance;
import org.camunda.bpm.model.bpmn.impl.instance.UserTaskImpl;
import org.camunda.bpm.model.bpmn.instance.MultiInstanceLoopCharacteristics;
import org.camunda.bpm.model.xml.instance.ModelElementInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.pb.wkflow.core.config.Constants.*;
import static com.pb.wkflow.core.config.Constants.VoteOption.VOTE_OPTION_AGREE;
import static com.pb.wkflow.core.config.Constants.VoteResult.*;
import static org.camunda.bpm.engine.impl.bpmn.behavior.MultiInstanceActivityBehavior.NUMBER_OF_COMPLETED_INSTANCES;
import static org.camunda.bpm.engine.impl.bpmn.behavior.MultiInstanceActivityBehavior.NUMBER_OF_INSTANCES;

/**
 * 多人会签
 * @author 苗益辉
 * @date 2022/5/25 19:06
 */
@Service
public class WorkflowMultiServiceImpl implements WorkflowMultiService {
    @Autowired
    HistoryService historyService;

    @Autowired
    RuntimeService runtimeService;

    @Autowired
    TaskService taskService;

    @Autowired
    WfSysProcInfoService wfSysProcInfoService;

    /**
     * 新增nrOfPassInstances字段统计通过人数，完成条件包含nrOfPassInstances和nrOfInstances
     * 每次提交计算当前条件是否满足，如果满足则提前结束
     * 每次提交计算未来条件是否满足，如果不满足则提前结束
     * @param delegateTask delegateTask
     */
    @Override
    public void dealVotingResult(DelegateTask delegateTask) {
        Map<String, Object> variables = delegateTask.getVariables();
        //统计完成人数，包括当前任务
        Integer nrOfCompletedInstances = (Integer) delegateTask.getVariable(NUMBER_OF_COMPLETED_INSTANCES) + 1;
        //统计所有人数
        Integer nrOfInstances = (Integer) delegateTask.getVariable(NUMBER_OF_INSTANCES);
        //统计通过人数
        Integer nrOfPassInstances = (Integer) delegateTask.getVariable(NUMBER_OF_PASS_INSTANCES);
        Object voteOption = variables.get(VOTE_OPTION);
        //没填审批意见，默认同意
        if(null == voteOption || VOTE_OPTION_AGREE.equals(voteOption)) {
            nrOfPassInstances = nrOfPassInstances + 1;
            delegateTask.setVariable(NUMBER_OF_PASS_INSTANCES, nrOfPassInstances);
            delegateTask.setVariableLocal(VOTE_RESULT, VOTE_RESULT_AGREE);
        }else {
            delegateTask.setVariableLocal(VOTE_RESULT, VOTE_RESULT_AGAINST);
        }
        //计算是否需要提前结束
        //计算当前条件是否满足，满足提前结束
        boolean nowPass = calculateCondition(delegateTask, nrOfPassInstances, nrOfInstances);
        Integer nrOfPassLateInstances = nrOfInstances - nrOfCompletedInstances + nrOfPassInstances;
        //如果为完成的都同意，判断条件是否满足，不满足则提前结束
        boolean futurePass = calculateCondition(delegateTask , nrOfPassLateInstances, nrOfInstances);
        if (nrOfCompletedInstances.equals(nrOfInstances)) {
            //如果全部完成，判断最终条件是否成立
            String voteResult = nowPass ? VOTE_RESULT_AGREE : VOTE_RESULT_AGAINST;
            updateVotingResult(delegateTask,voteResult);
        }else{
            //根据会签判断是否提前结束会签
            if(nowPass) {
                //如果当前条件满足，则通过
                updateVotingResult(delegateTask,VOTE_RESULT_AGREE);
            }else if (!futurePass) {
                //如果未来条件都不满足，则拒绝
                updateVotingResult(delegateTask, VOTE_RESULT_AGAINST);
            }
        }
    }

    /**
     * 获取会签的完成条件
     * 然后通过Juel计算条件是否成立
     * @param delegateTask delegateTask
     * @param nrOfPassInstances 投票通过人数
     * @param nrOfInstances 投票总人数
     * @return boolean
     */
    @Override
    public boolean calculateCondition(DelegateTask delegateTask, Integer nrOfPassInstances, Integer nrOfInstances) {
        Map<String, Object> variables = new HashMap<>(2);
        variables.put(NUMBER_OF_PASS_INSTANCES, nrOfPassInstances);
        variables.put(NUMBER_OF_INSTANCES, nrOfInstances);
        BpmnModelInstance bpmnModelInstance = delegateTask.getBpmnModelInstance();
        ModelElementInstance modelElementInstance = bpmnModelInstance.getModelElementById(delegateTask.getTaskDefinitionKey());
        UserTaskImpl userTask = (UserTaskImpl) modelElementInstance;
        Collection<MultiInstanceLoopCharacteristics> mls = userTask.getChildElementsByType(MultiInstanceLoopCharacteristics.class);
        MultiInstanceLoopCharacteristics loopCharacteristics = mls.iterator().next();
        String textContent = loopCharacteristics.getCompletionCondition().getTextContent();
        //调用Juel计算
        return JuelUtils.checkFormDataByRuleEl(textContent,variables);
    }

    /**
     * 更新未完成的审批任务，插入voteResult到任务流程变量
     * 更新历史任务，插入voteResult到任务流程变量
     * @param delegateTask delegateTask
     * @param votingResult agree,against
     */
    @Override
    public void updateVotingResult(DelegateTask delegateTask, String votingResult) {
        runtimeService.setVariable(delegateTask.getExecutionId(), VOTE_RESULT, votingResult);
        //会签提前结束，更新未完成的审批任务
        List<Task> tasks = taskService.createTaskQuery().processInstanceId(delegateTask.getProcessInstanceId())
                .taskCreatedOn(delegateTask.getCreateTime()).list();
        if(tasks != null) {
            for(Task task : tasks) {
                TaskEntity taskEntity = (TaskEntity) task;
                taskEntity.setVariableLocal(VOTE_RESULT, votingResult);
            }
        }
        //更新历史任务本地变量
        List<HistoricTaskInstance> hiTasks = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(delegateTask.getProcessInstanceId()).taskDefinitionKey(delegateTask.getTaskDefinitionKey())
                .taskDueDate(delegateTask.getCreateTime()).finished().list();
        ProcessEngineConfigurationImpl processEngineConfiguration = (ProcessEngineConfigurationImpl) delegateTask.getProcessEngine().getProcessEngineConfiguration();
        CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutorTxRequired();
        commandExecutor.execute(new Command<Void>() {
            @Override
            public Void execute(CommandContext context) {
                for(HistoricTaskInstance hiTask : hiTasks) {
                    PrimitiveTypeValueImpl.StringValueImpl typedValue = new PrimitiveTypeValueImpl.StringValueImpl(votingResult);
                    VariableInstanceEntity variableInstance = VariableInstanceEntity.create(VOTE_RESULT, typedValue, true);
                    variableInstance.setProcessInstanceId(delegateTask.getProcessInstanceId());
                    variableInstance.setExecutionId(hiTask.getExecutionId());
                    variableInstance.setTaskId(hiTask.getId());
                    VariableInstanceEntity.insert(variableInstance);
                }
                return null;
            }
        });
    }
}
